package cn.uncode.baas.server.database;

import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import cn.uncode.dal.mongo.MongoDB;
import cn.uncode.baas.server.cache.SystemCache;
import cn.uncode.baas.server.constant.Resource;
import cn.uncode.baas.server.dto.RestApp;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class DynamicMongoDB implements InitializingBean, MongoDB{
	
	private static Logger LOG = Logger.getLogger(DynamicMongoDB.class);

	private Map<Object, DB> resolvedMongoDB;
	
	private MongoDB defaultDB;


	/**
	 * Specify whether to apply a lenient fallback to the default MongoDB
	 * if no specific MongoDB could be found for the current lookup key.
	 * <p>Default is "true", accepting lookup keys without a corresponding entry
	 * in the target MongoDB map - simply falling back to the default MongoDB
	 * in that case.
	 * <p>Switch this flag to "false" if you would prefer the fallback to only apply
	 * if the lookup key was <code>null</code>. Lookup keys without a MongoDB
	 * entry will then lead to an IllegalStateException.
	 * @see #setTargetMongoDB
	 * @see #setDefaultTargetMongoDB
	 * @see #determineCurrentLookupKey()
	 */


	public void afterPropertiesSet() {
		this.resolvedMongoDB = new HashMap<Object, DB>();
	}


	/**
	 * Retrieve the current target MongoDB. Determines the
	 * {@link #determineCurrentLookupKey() current lookup key}, performs
	 * a lookup in the {@link #setTargetMongoDB targetMongoDB} map,
	 * falls back to the specified
	 * {@link #setDefaultTargetMongoDB default target MongoDB} if necessary.
	 * @see #determineCurrentLookupKey()
	 */
	public DB determineTargetMongoDB() throws UnknownHostException, MongoException{
    	Object lookupKey = determineCurrentLookupKey();
    	Assert.notNull(lookupKey, "MongoDB lookup key is null");
    	DB mongoDB = this.resolvedMongoDB.get(lookupKey);
    	if(null == mongoDB){
    		RestApp app = SystemCache.getRestApp(String.valueOf(lookupKey));
    		if(null != app){
    			if(app.getDbType() == Resource.REST_APP_DB_TYPE_MONGO){
    				String url = app.getDbURL();
        			if(StringUtils.isNotBlank(url)){
        				String[] urls = url.split("/");
        				String[] names = urls[0].split(":");
        				mongoDB = initDB(names[0], Integer.valueOf(names[1]), urls[1], app.getDbUsername(), app.getDbPassword());
        			}
    			}else if(app.getDbType() == Resource.REST_APP_DB_TYPE_DEFAULT){
    				mongoDB = defaultDB.getDB().getMongo().getDB(String.valueOf(lookupKey));
    			}
    			if(mongoDB != null){
					this.resolvedMongoDB.put(lookupKey, mongoDB);
				}
    		}
    	}
    	Assert.notNull(mongoDB, "MongoDB is null");
		return mongoDB;
    }

	/**
	 * Determine the current lookup key. This will typically be
	 * implemented to check a thread-bound transaction context.
	 * <p>Allows for arbitrary keys. The returned key needs
	 * to match the stored lookup key type, as resolved by the
	 * {@link #resolveSpecifiedLookupKey} method.
	 */
	protected Object determineCurrentLookupKey() {
		return DBContextHolder.getDbType();
	}
	

    public static DB initDB(String url, Integer port, String dbName, String user, String password) throws UnknownHostException,
            MongoException {
        Mongo m = null;
        if(null != port){
        	m = new Mongo(url, port);
        }else{
        	m = new Mongo(url);
        }
        DB db = m.getDB(dbName);
        if (!db.authenticate(user, password.toCharArray())) {
        	LOG.error("Couldn't Authenticate MongoDB!!!!!!.........");
            throw new MongoException("Couldn't Authenticate !!!!!!.........");
        }
        return db;
    }


	

	@Override
	public DB getDB(String username, String password) throws MongoException {
		try {
			DB db = determineTargetMongoDB();
			if (!db.authenticate(username, password.toCharArray())) {
	        	LOG.error("Couldn't Authenticate MongoDB!!!!!!.........");
	            throw new MongoException("Couldn't Authenticate !!!!!!.........");
	        }
			return db;
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		return null;
	}


	@Override
	public DB getDB() throws MongoException {
		try {
			return determineTargetMongoDB();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		return null;
	}


	public void setDefaultDB(MongoDB defaultDB) {
		this.defaultDB = defaultDB;
	}


	

    
}
